import { Button, Spin, message } from 'antd';
import { useEffect, useRef, useState } from 'react';

const mp3Url = '/cyberpunk.mp3';

const FetchAudio = () => {
    const [paused, setPaused] = useState(true);
    const sourceRef = useRef<AudioBufferSourceNode>();
    const audioContextRef = useRef<AudioContext>();
    const audioArrBufRef = useRef<ArrayBuffer>();
    const audioBufRef = useRef<AudioBuffer>();
    const [fetched, setFetched] = useState(false);

    const createAudioContext = () => {
        if (!audioContextRef.current) {
            audioContextRef.current = new AudioContext();
        }
        return audioContextRef as React.MutableRefObject<AudioContext>;
    }

    const getAudioBuf = async () => {
        try {
            /** 请求 mp3 文件 */
            const response = await fetch(mp3Url);
            const buffer = await response.arrayBuffer();
            audioArrBufRef.current = buffer;
            setFetched(true);
        } catch (error) {
            message.error('音频加载失败啦 >_<');
            console.error('error: ', error);
        }
    }

    useEffect(() => {
        /** 获取音频数据 */
        getAudioBuf();
        return () => {
            sourceRef.current?.stop();
            /** 关闭一个音频环境，释放任何正在使用系统资源的音频。 */
            audioContextRef.current?.close();
            sourceRef.current = void 0;
            audioContextRef.current = void 0;
        }
    }, []);

    const handleClick = async () => {
        if (!fetched || !audioArrBufRef.current)   return message.error('音频加载失败啦 >_<');
        try {
           /**
             * 恢复之前暂停播放的音频，像 Safari 浏览器音频有上下文有时会处于暂停状态（一些奇怪的边缘问题）
             * 这是一个模糊不清的事情（比如用户以某种方式拒绝音频许可、或者以某种方式离开站点并返回）
             * 这行代码以确保音频上下文处于运行状态
            */
            const audioContext = createAudioContext().current;
            await audioContext.resume();
            /** 异步解码音频文件中的 ArrayBuffer（ArrayBuffer -> AudioBuffer） */
            const buf = audioBufRef.current || await audioContext.decodeAudioData(audioArrBufRef.current);
            audioBufRef.current = buf;
            /** 创建 bufferSourceNode */
            if (paused) {
                const source = audioContext.createBufferSource();
                /** 连接终点 */
                source.connect(audioContext.destination);
                /** 给音频数据 */
                source.buffer = buf;
                source.loop = true;
                /** 0 代表立即播放，并在 audioContext 暂停处开始播放 */
                source.start(0, audioContext.currentTime);
                sourceRef.current = source;
                setPaused(false);
            } else {
                setPaused(true);
                sourceRef.current?.stop();
            }
        } catch (error) {
            console.error('error: ', error);
        }
    }

    return (
        <Spin tip="正在加载音频文件" spinning={!fetched}>
            <div>
                <Button onClick={handleClick}>{ paused ? '播放' : '暂停' }</Button>
            </div>
        </Spin>
    );
};

export default FetchAudio;
